#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>

#define DEBUG 1

#ifndef DEBUG
    #error You should define DEBUG as 0 or 1!
#endif

#if (DEBUG == 1)
    #define OUT
    #define ASSERT( cond ) \
        if (!(cond))\
        {\
            printf ("Oops! Assertion <%s> failed \non %d line of %s file.\n", \
                    #cond, __LINE__, __FILE__);\
            exit (1);\
        }
    #define GRAM_ASSERT( cond ) \
        if (!(cond))\
        {\
            printf ("Oops! Assertion <%s> failed \non %d line of %s file.\n" \
                    "Please check the correspondance of input expression and used grammar.\n", \
                    #cond, __LINE__, __FILE__);\
            printf ("Current symbol: '%c'\n", *s);\
            exit (1);\
        }
#else
    #define OUT if (0)
    #define ASSERT( cond )
#endif

//#define stdout The_Log

#define NODE_CHECK( node ) \
   if (Node_Is_OK (node, NULL) != NODE_OK)\
    {\
        Node_Dump (The_Log, node, 0);\
        ASSERT (0);\
    }

#define TREE_CHECK( tree ) \
    if (Tree_Is_OK (tree) != TREE_OK) \
    {\
        Tree_Dump (The_Log, tree);\
        ASSERT (0);\
    }

extern FILE* The_Log;
extern FILE* outstream;
const unsigned int Poison = 0xBAD;
const int Max_Len = 100;

typedef struct tree_t
{
    double info;
//    int size;
    int type;

    tree_t* left;
    tree_t* right;
    tree_t* parent;
} tree_t;

typedef struct root_t
{
    int size;

    tree_t* root;
} root_t;

typedef struct token
{
    int type;
    double value;
} token_t;

extern token_t* s;

enum ERR_NODE
{
    NODE_ERR = 999,
    NODE_OK = 1000
};

enum ERR_TREE
{
    TREE_ERR = 1999,
    TREE_OK = 2000,
};

enum TYPES
{
    NO_TYPE = 0,
    NUMBER = 1,
    OPERATION = 2,
    VARIABLE = 3,
    BRACKET = 4,
    DIFF_TYPE = 5,
    EOF_TYPE = 6
};

enum SYMBOLS
{
    ADD  = '+',
    SUB  = '-',
    MUL  = '*',
    DIV  = '/',
    L_BR = '(',
    R_BR = ')',
    DIFF = 'D',
    X    = 'X'
};

int Get_String (FILE* instream, token_t* str);
int Get_Token (FILE* instream, token* str);
int Scan_For_Number (FILE* instream);

int Get_G4 (token_t* str, root_t* tree);
int Get_E_G4 (root_t* tree, tree_t* node);
int Get_T_G4 (root_t* tree, tree_t* node);
int Get_P_G4 (root_t* tree, tree_t* node);
int Get_N_G4 (root_t* tree, tree_t* node);

int  Node_Construct     (tree_t** new_node, double info);
int  Node_Destruct      (tree_t* node);
int  Node_Is_OK         (tree_t* node, int* size);
void Node_Dump          (FILE* outstream, tree_t* node, int depth);
int  Node_Print         (FILE* outstream, tree_t* node);
int  Node_Print_Postfix (FILE* outstream, tree_t* node);
int  Node_Print_Dot     (FILE* outstream, tree_t* node);

int  Tree_Construct     (root_t** new_tree, double info);
int  Tree_Destruct      (root_t* tree);
int  Tree_Is_OK         (root_t* tree);
void Tree_Dump          (FILE* outstream, root_t* tree);
int  Tree_Print         (FILE* outstream, root_t* tree);
int  Tree_Print_Postfix (FILE* outstream, root_t* tree);
int  Tree_Print_Dot     (FILE* outstream, root_t* tree);
int  Tree_Find          (root_t* tree, tree_t* node, int type);

int  Tree_Optimizer  (root_t* tree);
int  Compress_Consts (root_t* tree, tree_t* node);
int  Del_Zerones     (root_t* tree, tree_t* node);

int  Node_Add_Left  (root_t* tree, tree_t* node, tree_t* child);
int  Node_Add_Right (root_t* tree, tree_t* node, tree_t* child);
int  Node_Delete    (root_t* tree, tree_t* node);

int  Node_Copy      (tree_t* dest, tree_t* source);
int  Node_Clear     (tree_t* node);

tree_t*  Diff          (root_t* tree, tree_t* node);
tree_t* Create_Op_Node (root_t* tree, int op, tree_t* left, tree_t* right);
tree_t* Node_Copy (tree_t* source);
